#pragma once

#include "common.h"
#include "ntdproto.h"
#include "spdlimit.h"
#include "timer.h"

class IUdpClientListener
{
public:
	enum{
		ERR_HS_TIMEOUT,
		ERR_FIN_TIMEOUT,
	};
	virtual void HandleResult(uint32 sendrate, uint32 recvrate, bool inc, uint32 loss) = 0;
	virtual void HandleError(uint32 err_code) = 0;
};

class UDPClient : public NtdProtoParser, public INTDTimerListener, public ITokenBucketListner
{
public:
	UDPClient(IUdpClientListener* pListen=NULL);
	virtual ~UDPClient();

	void Reset();

	bool Connect(const std::string& ip, int port);
	void Close();

	int32 OnWrite();
	void OnRead();

	virtual void OnTimer(NTDTimer* pTimer);
	virtual void OnTokenFilled(TokenBucket * pBucket);
	virtual void OnTokenEmpty(TokenBucket * pBucket);

	void OnFinish();

	void SetRateLimit(uint32 limit);//Bytes/sec
	void SetDuration(float t);//g_settings.m_probeTime

	void SetPacketPairMode(bool bPacketPair){
		//Will Invalidate ratelimit
		m_bPacketPair = bPacketPair;
		if(m_bPacketPair){
			SetRateLimit(1024);//1KB/s
		}
	}


	uint32 GetTotal() { return m_total; }
	uint32 GetSendingrate() { 
		//return m_per_total*1000/(m_totaltick/1000); 
		return m_per_total/(m_timecost/1000.0/1000.0);
	}
	uint32 GetCalcdInterval() { return m_pktInterval; }

protected:

	virtual void HandleProtoFeedback(NtdProtoFeedback * proto, void* pContext);
	virtual void HandleHandShakeResp(NtdProtoHandShakeResp * proto, void* pContext);
	virtual void HandleFinResp(NtdProtoFinResp * proto, void* pContext);
private:
	int32 SendData(); 
	int32 SendHandShake();
	int32 SendFin();

private:

	int32 m_seq;
	
	event_base * m_base;
	struct event *m_ev;
	struct event *m_writeEv;
	//struct event m_timeout;

	evbuffer * m_evbuf_input;
	evbuffer * m_evbuf_output;

	NtdProtoData m_protodata;

	evutil_socket_t m_socket;
    struct sockaddr_in m_dest;

	int32 m_owd_base;

	uint32 m_ratelimit;
	uint64 m_pktInterval;//us
	uint64 m_lastsent;//us

	bool m_bInited;
	uint32 m_hsInterval;

	bool m_bFin;
	uint32 m_total;
	uint32 m_per_total;
	//uint64 m_totaltick;//not used now for it will introduce error 4 times larger, use m_timecost instead
	uint64 m_starttick;
	uint64 m_timecost; 

	uint64 m_timeout_us;

	bool m_bPacketPair;

	IUdpClientListener* m_pListen;

	//NTDTimer m_loop_timer;
	NTDTimer m_tick;
	TokenBucketCfg m_cfg;
	TokenBucket m_send_bucket;
	int32 m_ptk_size;
};